/*
 * File: getEvent.c
 *
 * Author: Peter Horak
 * Purpose: get GPS data and print it along with its RSSI, print other
 * status messages, and view the contents of one of the DNT's registers
 *
 * To use:
 * gcc getEvent.c -o getEvent
 * ./getEvent > data.txt
 * cat data.txt | tr '*' '\n' | grep '. $GPGGA,.........,..........,N,...........,W,.,..,....,.....,M,.....,M,,*' | awk '{if (length($0) < 80) print $0}' | tr ' ' ',' | awk -F',' '{print $1,$4,$6}' > parsed_data.txt
 */

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* For strlen, strncpy, and memset */
#include <stdlib.h>  /* For calloc */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <dirent.h>  /* read directories and devices */
#include <signal.h>  /* For catching ^C interrupt */
#include <sys/time.h>/* For gettimeofday() */

#define BAUDRATE B230400
//#define BAUDRATE B115200

// Recieve a protocol message and print it in hex
void receive(int fd)
{    
    int iRawCh = 0;
    char result[2] = {0,0};
    
    while (result[0] != (char)0xFB) read(fd, result, 1);
    //fprintf(stderr, "%X ", result[0]);
    while (iRawCh < 1) iRawCh = read(fd, result, 1);
    //fprintf(stderr, "%X ", result[0]);
    
    int bytes = (int)result[0];
    int count = 0;
    iRawCh = 0;
    while (count < bytes)
    {
        iRawCh = read(fd, result, 1);
        if (iRawCh > 0)
        {
            fprintf(stderr, "%X ", result[0]);
            ++count;
        }
        else usleep(50000);
    }
    if (result[0] == (char)0x10) fprintf(stderr," (Entering protocol mode)");
    if (result[0] == (char)0x11) fprintf(stderr," (Exiting protocol mode)");
    fprintf(stderr, "\n");
    fflush(stderr);
}

// Detect an event and print an appropriate message
void get_data(int fd)
{
    int iRawCh = 0;
    char result[2] = {0,0};
    int dataline = 0; // false by default
    
    // Message indicator byte
    while (result[0] != (char)0xFB) read(fd, result, 1);
    // Message length
    while (iRawCh < 1) iRawCh = read(fd, result, 1);
    
    iRawCh = 0; // Bytes (chars) received
    int bytes = (int)result[0];
    
    if (bytes < 1) return; // Always read four bytes (the message type)
    int count = 1;
    // Message type
    while (iRawCh < 1) iRawCh = read(fd, result, 1);
    if (result[0] == (char)0x26) // RxData, print the data as characters
    {
        static char GPSstring[100];
        static int GPSpos = 0;
        int rssi = 0;
        while (count < bytes)
        {
            iRawCh = read(fd, result, 1);
            if (iRawCh > 0)
            {
                // Ignore addr (first 3 bytes)
                if (count == 4) // RSSI
                {
                    rssi= ((result[0] & 0x80) ? -(-result[0] & 0xFF) : result[0]);
                }
                else if (count > 4) // data
                {
                    if (result[0] == '#')
                    {
                        fprintf(stderr, "%d ", rssi);
                        fprintf(stdout, "%d ", rssi);
                        GPSstring[GPSpos] = '\0';
                        fprintf(stderr, "%s", GPSstring);
                        fprintf(stdout, "%s", GPSstring);
                        fflush(stdout);
                        GPSpos = 0;
                    }
                    else
                    {
                        GPSstring[GPSpos++] = result[0];
                    }
                }
                ++count;
            }
            else usleep(50000);
        }
    }
    else if (result[0] == (char)0x27) // Announcement, print the announcement
    {
        if (count >= bytes) return;
        iRawCh = 0;
        // Status code
        while (iRawCh < 1) iRawCh = read(fd, result, 1);
        ++count;
        switch (result[0]) {
            case ((char)0xA0):
                fprintf(stderr, "Radio has completed startup initialization.");
                break;
            case ((char)0xA2):
                fprintf(stderr,"Base: a child has joined the network.");
                break;
            case ((char)0xA3):
                fprintf(stderr,"Remote: joined a network, ready for data.");
                break;
            case ((char)0xA4):
                fprintf(stderr,"Remote: exited network.");
                break;
            case ((char)0xA5):
                fprintf(stderr,"Remote: the base has rebooted.");
                break;
            case ((char)0xA7):
                fprintf(stderr,"Base: remote has left the network.");
                break;
            case ((char)0xA8):
                fprintf(stderr,"Base: heartbeat received from remote.");
                break;
            case ((char)0xA9):
                fprintf(stderr,"Base: router heartbeat timeout.");
                break;
            default:
                fprintf(stderr,"Status code %X unknown.", result[0]);
        }
        
        // Read any additional fields in hex
        while (count < bytes)
        {
            iRawCh = read(fd, result, 1);
            if (iRawCh > 0)
            {
                ++count;
            }
            else usleep(50000);
        }
        fprintf(stderr, "\n");
    }
    else // Other, print out the message in hex
    {
        while (count < bytes)
        {
            iRawCh = read(fd, result, 1);
            if (iRawCh > 0)
            {
                fprintf(stderr, "%X ", result[0]);
                ++count;
            }
            else usleep(50000);
        }
        fprintf(stderr, "\n");
    }
}

int main(int argc, char *argv[]){
    
    char * devPort = NULL;
    
    // Find Dev
    DIR *dir = opendir ("/dev");
    struct dirent *ent;
    while ((ent = readdir (dir))>0)
    {
        if ((strncmp("tty.usbmodem",ent->d_name,12)==0) ||
            (strncmp("tty.KeySerial",ent->d_name,12)==0) ||
            (strncmp("tty.USA",ent->d_name,7)==0))
        {
            devPort = ent->d_name;
            break;
        }
    }
    closedir (dir);
    if (devPort == NULL)
    {
        fprintf(stderr, "Failed to find a connected serial device\n");
        return 1; // failure
    }
    
    // Add "/dev/" to the port name
    char* fullPort = calloc(strlen(devPort) + 6,sizeof(char));
    sprintf(fullPort, "/dev/%s", devPort);
    fprintf(stderr, "Connecting to serial port \"%s\"\n",fullPort);
    
    int fd;
    fd = open(fullPort,(O_RDWR | O_NOCTTY | O_NDELAY));
    if (fd < 0)
    {
        fprintf(stderr, "Failed to connect to device.\n");
        return 1;
    }

    free(fullPort); // No longer needed
    
    //init port==================
    struct termios options;
    memset(&options,0,sizeof(options));
    // Set the baud rate
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);
    // Enable the receiver and set local mode...
    options.c_cflag=CS8|CREAD|CLOCAL;
    options.c_cc[VMIN]=1;
    options.c_cc[VTIME]=5;
    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);
    //end init port==============

    fcntl(fd, F_SETFL, FNDELAY); // don't block serial read
    fcntl(fileno(stdin), F_SETFL, FNDELAY); // don't hang on stdin
    //fcntl(fileno(stdout), F_SETFL, FNDELAY);
    
    char cmd[10];
    memset(cmd, 0, 10);
    
    usleep(100000);
    
    // Enter protocol mode
    strncpy(cmd,"   DNTCFG", 9);
    cmd[0]=0xFB; cmd[1]=0x07; cmd[2]=0x00;
    write(fd, cmd, 9);
    receive(fd);
    
//    // Request register contents
//    // Format: sentinel msg_len(-2) action reg# bank# num_bytes_to_read
//    cmd[0]=0xFB; cmd[1]=0x04; cmd[2]=0x03; cmd[3]=0x13; cmd[4]=0x02; cmd[5] = 0x01;
//    write(fd, cmd, 6);
//    receive(fd);
    
    // Print event messages and wait for ^C to continue
    while (1)
        get_data(fd);
    
    // Exit protocol mode
    cmd[0]=0xFB; cmd[1]=0x01; cmd[2]=0x01;
    write(fd, cmd, 3);
    receive(fd);
    
    fflush(stdout);
    close(fd);
    return 0; // success
}
